
use vhdl_lang::ast::*;

use std::fmt;

use super::utils::{to_str};
use super::spinal::NameSpinal;

// use std::fmt;
// use vhdl_lang::SrcPos;

#[derive(PartialEq, Debug, Clone)]
pub enum Expr {
    Binary(Binary, Box<Expr>, Box<Expr>),
    Unary(Unary, Box<Expr>),

    /// LRM 9.3.3 Aggregates
    // Aggregate(Vec<ElementAssociation>),

    /// LRM 9.3.5 Qualified exprs
    Qualified(Box<QualifiedExpr>),

    /// LRM 8 Names
    Name(NameSpinal),

    /// LRM 9.3.2 Literals
    Literal(LiteralSpinal),

    /// LRM 9.3.7 Allocators
    // New(Box<WithPos<Allocator>>),
    Nosupport,
}
impl std::default::Default for Expr {
    fn default() -> Self {
        Expr::Nosupport
    }
}
impl<'a> From<&'a Expression> for Expr {
    fn from(e:&Expression) -> Self {
        match e {
            Expression::Binary(op, a, b) => 
                Expr::Binary(*op,
                    Box::new(Expr::from(&a.item)),
                    Box::new(Expr::from(&b.item))
                ),
            Expression::Unary(op, a) =>
                Expr::Unary(*op,
                    Box::new(Expr::from(&a.item))
                ),
            Expression::Qualified(q) =>
                Expr::Qualified(Box::new(QualifiedExpr::from(q))),
            Expression::Name(n) => 
                Expr::Name(NameSpinal::from(&**n)),
            Expression::Literal(l) =>
                Expr::Literal(LiteralSpinal::from(l)),
            _ => Expr::Nosupport,
        }
    }
}
impl fmt::Display for Expr {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Expr::Binary(op,a,b) => write!(f,"({} {:?} {})",a,op,b),
            Expr::Unary(op,a) => write!(f,"({:?} {})",op,a),
            Expr::Qualified(a) => write!(f,"{}",a),
            Expr::Name(a) => write!(f,"{}", a),
            Expr::Literal(a) => write!(f, "{}", a),
            _ => write!(f, ""),
        }
    }
}
impl Expr {
    fn ve2str(s:&Vec<Expr>) -> String {
        let r: Vec<String> = s.iter().map(|e| format!("{}",e)).collect();
        r.join(",")
    }
}

#[derive(PartialEq, Debug, Clone)]
pub struct QualifiedExpr {
    pub name: String,
    pub expr: Box<Expr>,
}
impl<'a> From<&'a Box<QualifiedExpression>> for QualifiedExpr {
    fn from(l:&Box<QualifiedExpression>) -> Self {
        QualifiedExpr {
            name: to_str(&l.name),
            expr: Box::new(Expr::from(&l.expr.item)),
        }
    }
}
impl fmt::Display for QualifiedExpr {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}->{}",self.name,*self.expr)
    }
}
#[derive(PartialEq, Debug, Clone)]
pub enum LiteralSpinal {
    String(String),
    BitString(BitStringSpinal),
    Character(u8),
    AbstractLiteral(AbstractLiteral),
    // Physical(AbstractLiteral, Symbol),
    Null,
}
impl<'a> From<&'a Literal> for LiteralSpinal {
    fn from(l:&Literal) -> Self {
        match l {
            Literal::String(s)    => LiteralSpinal::String(format!("{}",s)),
            Literal::BitString(b) => LiteralSpinal::BitString(BitStringSpinal::from(b)),
            Literal::Character(u) => LiteralSpinal::Character(*u),
            Literal::AbstractLiteral(u) => LiteralSpinal::AbstractLiteral(*u),
            Literal::Null          => LiteralSpinal::Null,
            Literal::Physical(_,_) => LiteralSpinal::Null,
        }
    }
}
impl fmt::Display for LiteralSpinal {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            LiteralSpinal::String(s) => write!(f, "{}", s),
            LiteralSpinal::BitString(b) => write!(f, "{}", b),
            LiteralSpinal::Character(c) => write!(f,"{}",c),
            _ => write!(f,"Invalid"),
        }
    }
}
/// LRM 15.8 Bit string literals
#[derive(PartialEq, Debug, Clone)]
pub struct BitStringSpinal {
    pub length: Option<u32>,
    pub base: BaseSpecifier,
    pub value: String,
}

impl<'a> From<&'a BitString> for BitStringSpinal {
    fn from(b: &BitString) -> Self {
        BitStringSpinal {
            length: b.length,
            base: b.base,
            value: format!("{}", b.value),
        }
    }
}
impl fmt::Display for BitStringSpinal {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if let Some(l) = self.length {
            write!(f, "{}{}{}",l,BaseSpecifier2str(&self.base),self.value)
        } else {
            write!(f, "{}{}",BaseSpecifier2str(&self.base),self.value)
        }
    }
}
        
fn BaseSpecifier2str(base:&BaseSpecifier) -> &'static str {
    match base {
        BaseSpecifier::B => "B",
        BaseSpecifier::O => "O",
        BaseSpecifier::X => "X",
        BaseSpecifier::UB => "UB",
        BaseSpecifier::UO => "UO",
        BaseSpecifier::UX => "UX",
        BaseSpecifier::SB => "SB",
        BaseSpecifier::SO => "SO",
        BaseSpecifier::SX => "SX",
        BaseSpecifier::D => "D",
    }
}

#[cfg(test)]
mod test {
    use vhdl_lang::data::Latin1String;
    use vhdl_lang::ast::*;

    use super::*;

    #[test]
    fn test_bit_string() {
        let b = BitString {
            length: Some(8),
            base: BaseSpecifier::X,
            value:Latin1String::new(b"8001"),
        };

        assert_eq!(
            BitStringSpinal::from(&b),
            BitStringSpinal {
                length: Some(8),
                base: BaseSpecifier::X,
                value:String::from("8001"),
            }
        )        
    }
}

